/*
Copyright 2010,2011 Matt Van Der Westhuizen

This file is part of CGLL.

CGLL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

CGLL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with CGLL.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.chaoticengine.cgll.entity.component.effects;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.chaoticengine.cgll.entity.component.ActiveRenderComponent;
import org.chaoticengine.cgll.input.Command;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.StateBasedGame;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;

/**
 * This class is useful for adding an endlessly scrolling background (vertical
 * or horizontal) to your game.
 *
 * @author Matt v.d. Westhuizen
 */
@Root
public class ScrollingBackgroundComponent extends ActiveRenderComponent {
    public static enum ScrollDirection {LEFT, RIGHT, UP, DOWN};

    protected HashMap<String, Command> commands = new HashMap<String, Command>();
    protected List<Image> images = new ArrayList<Image>();
    protected List<Float> positions = new ArrayList<Float>();
    protected ScrollDirection direction = ScrollDirection.DOWN;
    protected float scrollSpeed = 20.0f;
    protected float scrollSpeedMs = scrollSpeed / 1000.0f;
    protected boolean positionsInvalid = false;

    public ScrollingBackgroundComponent() {}

    @ElementList(name="images", entry="image")
    public List<String> getImages() {
        List<String> result = new ArrayList<String>(images.size());
        for (Image img : images) {

            result.add(img.getResourceReference());
        }
        return(result);
    }

    @ElementList(name="images", entry="image")
    public void setImages(List<String> newImages) {
        this.images.clear();

        for (String filename : newImages) {
            try {
                Image img = new Image(filename);
                this.images.add(img);
            } catch (SlickException sEx) {
                System.out.println("Error: ScrollingBackgroundComponent failed to load image '" + filename + "'.");
                sEx.printStackTrace();
                System.exit(-1);
            }
        }

        positionsInvalid = true;
    }

    @Element(name="direction")
    public ScrollDirection getDirection() {
        return(direction);
    }

    @Element(name="direction")
    public void setDirection(ScrollDirection dir) {
        if (dir != direction) {
            positionsInvalid = true;
        }
        direction = dir;
    }

    /** Returns the scroll speed in pixels per second. */
    @Element(name="speed")
    public float getScrollSpeed() {
        return(scrollSpeed);
    }

    /** Sets the scroll speed to a new speed (specified in pixels per second). */
    @Element(name="speed")
    public void setScrollSpeed(float f) {
        scrollSpeed = f;
        scrollSpeedMs = scrollSpeed / 1000.0f;
    }

    @Override
    public void update(GameContainer gc, StateBasedGame sb, int delta) {
        super.update(gc, sb, delta);

        // Recalculate positions if invalid
        if (positionsInvalid) {
            positions.clear();

            float p = 0.0f;
            positions.add(p);

            for (int i = 1; i < images.size(); i++) {
                if (direction == ScrollDirection.LEFT) {
                    p += images.get(i - 1).getWidth();
                } else if (direction == ScrollDirection.RIGHT) {
                    p -= images.get(i - 1).getWidth();
                } else if (direction == ScrollDirection.UP) {
                    p += images.get(i - 1).getHeight();
                } else if (direction == ScrollDirection.DOWN) {
                    p -= images.get(i - 1).getHeight();
                }

                positions.add(p);
            }
            
            positionsInvalid = false;
        }

        // Updated positions
        float increment = delta * scrollSpeedMs;

        for (int i = 0; i < images.size(); i++) {
            float p = positions.get(i);

            if (direction == ScrollDirection.LEFT) {
                p -= increment;
                positions.set(i, p);
            } else if (direction == ScrollDirection.RIGHT) {
                p += increment;
                positions.set(i, p);
            } else if (direction == ScrollDirection.UP) {
                p -= increment;
                positions.set(i, p);
            } else if (direction == ScrollDirection.DOWN) {
                p += increment;
                positions.set(i, p);
            }
        }

        // Check for fall-off
        boolean swapout = false;
        if (direction == ScrollDirection.LEFT) {
            if (positions.get(0) < -gc.getWidth()) {
                swapout = true;
            }
        } else if (direction == ScrollDirection.RIGHT) {
            if (positions.get(0) > gc.getWidth()) {
                swapout = true;
            }
        } else if (direction == ScrollDirection.UP) {
            if (positions.get(0) < -gc.getHeight()) {
                swapout = true;
            }
        } else if (direction == ScrollDirection.DOWN) {
            if (positions.get(0) > gc.getHeight()) {
                swapout = true;
            }
        }

        if (swapout) {
            float newPos = positions.get(positions.size() - 1);

            if (direction == ScrollDirection.LEFT) {
                newPos += images.get(images.size() - 1).getWidth();
            } else if (direction == ScrollDirection.RIGHT) {
                newPos -= images.get(images.size() - 1).getWidth();
            } else if (direction == ScrollDirection.UP) {
                newPos += images.get(images.size() - 1).getHeight();
            } else if (direction == ScrollDirection.DOWN) {
                newPos -= images.get(images.size() - 1).getHeight();
            }

            positions.add(newPos);
            positions.remove(0);

            images.add(images.get(0));
            images.remove(0);
        }
    }

    @Override
    public void render(GameContainer gc, StateBasedGame sbg, Graphics grphcs) {
        if (positions.isEmpty()) {
            return;
        }
        
        if (direction == ScrollDirection.LEFT) {
            float w = positions.get(0);
            int i = 0;
            while ((w < gc.getWidth()) && (i < images.size())) {
                Image img = images.get(i);
                grphcs.drawImage(img, positions.get(i), 0);
                w += img.getWidth();
                i++;
            }
        } else if (direction == ScrollDirection.RIGHT) {
            float w = -positions.get(0);
            int i = 0;
            while ((w < gc.getWidth()) && (i < images.size())) {
                Image img = images.get(i);
                grphcs.drawImage(img, positions.get(i), 0);
                w += img.getWidth();
                i++;
            }
        } else if (direction == ScrollDirection.UP) {
            float h = positions.get(0);
            int i = 0;
            while ((h < gc.getHeight()) && (i < images.size())) {
                Image img = images.get(i);
                grphcs.drawImage(img, 0, positions.get(i));
                h += img.getHeight();
                i++;
            }
        } else if (direction == ScrollDirection.DOWN) {
            float h = -positions.get(0);
            int i = 0;
            while ((h < gc.getHeight()) && (i < images.size())) {
                Image img = images.get(i);
                grphcs.drawImage(img, 0, positions.get(i));
                h += img.getHeight();
                i++;
            }
        }
    }

    @Override
    public Map<String, Command> getCommands() {
        return(commands);
    }
}
